#include "frontendCommunicator.hh"

#include <string>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h> //client (gethostbyname())

#include <iostream>
#include <fstream>
#include <stdlib.h> //atoi atol ...etc


using namespace std;

frontendCommunicator::frontendCommunicator(userModularFunctionBase* t,
        string task_name,
        size_t grain_size,
        string kaapiTask_dir,
        string grid_data_dir,
        string machine_file_addr, 
        string decoder_addr,
        int decoder_port,
        string grid_addr,
        int grid_port,
        string job_key,
        e_where w,
        string grid_public){
    

    task = t;
    taskName = task_name;
    grain = grain_size;
    kaapiTaskDir = kaapiTask_dir;
    gridDataDir = grid_data_dir;
    grid_machine_file_addr = machine_file_addr;

    decoderAddr = decoder_addr;
    decoderPort = decoder_port;
    gridAddr = grid_addr;
    gridPort = grid_port;

    oarsh_job_key_file = job_key;
    where = w; 
    grid_public_machine = grid_public;
}
e_status frontendCommunicator::connectToDecoder(){

    return connectToClient("decoder", decoderAddr.c_str(), decoderPort, &decoderSock);

}
e_status frontendCommunicator::writeToDecoder(size_t k, size_t errors){
    stringstream send_data;
    send_data << "d," << k << ',' << errors << '|';
    if (send(decoderSock, send_data.str().c_str(),send_data.str().length(), 0) == -1)
    {   perror("send");
        return ERROR;
    }
    return SUCCESS;
}
e_status frontendCommunicator::readFromDecoder(integer* res, size_t*  errors){
    char *stringToken ;
    string result;
    int bytes_recieved;
    int counts = 0;

    if((int)resultQueue.size() < 1){

        bytes_recieved = recv_to(decoderSock,recv_data,FRONTEND_BUFFER_SIZE,0,FRONTEND_DECODER_TIMEOUT);
        if(bytes_recieved == 0){ //means socket is closed!
            cerr << "decoder is disconnected !!" << endl;
            terminate();
            return DISCONNECTED; 
        }else if(bytes_recieved == -1){
            cerr << "cannot read from decoder!! " << endl;
            terminate();
            return ERROR;
        }else if(bytes_recieved == -2){
            //cerr << "decoder timeout" << endl;
            return TIMEOUT;
        }

        recv_data[bytes_recieved] ='\0';//define end of the string

        cout << "[front-end: communicator] received data: " << recv_data << endl;

        stringToken = strtok(recv_data , "|");
        while( stringToken != NULL){

            result.assign(stringToken);
            resultQueue.push(result);
            stringToken = strtok(NULL,"|");
        }
    }

    if(resultQueue.size() >= 1){

            
        if(resultQueue.front()[0] == 'r'){

                //fitch the content
               
            stringToken = strtok((char*) resultQueue.front().c_str(), ",");
            while(stringToken != NULL){
                if(counts == 1)// the first should be r
                    res->set_str(stringToken, 10);
                else if( counts == 2)
                    *errors = atol(stringToken);

                stringToken = strtok(NULL,",");//move to next token

                counts ++ ;
            }
            if(counts != 3){

                cerr << "[front-end communicator] few command arguments [buffer might been exeeded]" << endl;
                *res = 0;
                *errors = 0;
                resultQueue.pop();
                return ERROR;
            }
//            cout << "res "  << *res << " errors " << *errors << endl;
            resultQueue.pop();

            return SUCCESS;

        }else if( resultQueue.front()[0] == '$' ){
            resultQueue.pop();cout << "terminated " << endl;
            return TERMINATED; //terminated

        }else //wrong command syntax
        {
            cerr << "[front-end communicator] wrong result syntax [buffer might been exeeded]" << endl;
            resultQueue.pop();
            return ERROR;
        }
    }
    return ERROR;
}
e_status frontendCommunicator::terminateDecoder(){
    
    if (send(decoderSock, "$",2, 0) == -1)
    {
        perror("send");
        return ERROR;
    }
    return SUCCESS;
}
e_status frontendCommunicator::terminate(){

    return terminateSocket(decoderSock);


}
e_status frontendCommunicator::dispatchGridTasks(size_t first, size_t last){

    stringstream cmd, karun, rsync;
    int i;

    int y;
    errfile = "/tmp/gridError";

    ofstream f;
    f.open(errfile.c_str(), ios::trunc);
    f.close(); // just to clear the file

    if(where == LOCAL){
        cmd << " karun -t 1 " << kaapiTaskDir <<"/kaapiTask " << gridDataDir << '/' << taskName <<' ' <<  first << ' ' << last << ' ' << grain << ' ' << task->getName() << ' ';
        for(i=0; i < task->getArguments().size(); i++){
            if(task->getFiles().size() > 0)
                for(y=0; y < task->getFiles().size(); y++){
                    if(i == (task->getFiles()[i]).first)
                        cmd << ' ' << (task->getFiles()[i]).second;
                    else
                        cmd << ' ' << task->getArguments()[i];
                }
            else
                cmd << ' ' << task->getArguments()[i];
        }
      //  cmd << " 2> " << errfile << "; rm -f " << errfile << ") > /dev/null &" ;
    }
    else if(where == GRID5000){
        //sending input files to grid
        //cout << "its grid " <<endl;
        if(task->getFiles().size() > 0){
            cout << "front-end communicator] sending inputs files to grid 5000 .." << endl;
            rsync << " rsync -avzu ";
            for(i=0; i < task->getFiles().size(); i++)
                rsync << (task->getFiles()[i]).second << ' ';

            rsync << gridAddr << ':' << "'" << gridDataDir<< "'"; //distination
            cout << "cmd: " << rsync.str() << endl;
            system(rsync.str().c_str());
        }

        karun <<" karun -t 1 -v  -f " << grid_machine_file_addr << " --wb ssh=oarsh " << kaapiTaskDir << "/kaapiTask ";
        karun << gridDataDir << '/' << taskName  << ' ' << first << ' ' << last << ' ' << grain << ' '<< task->getName() << ' ';
        // adding arguments + adding new directory path to file arguments
        for(i=0; i < task->getArguments().size(); i++){
            if(task->getFiles().size() > 0)
                for(y=0; y < task->getFiles().size(); y++){

                    if(i == (task->getFiles()[i]).first)
                        karun << ' ' << gridDataDir  << '/' << task->getArguments()[i];
                    else
                        karun << ' ' << task->getArguments()[i];
                }
            else
                karun << ' ' << task->getArguments()[i];
        }

        //dont forget (
        cmd << "( ssh " << gridAddr << " 'ssh frontend 'oarsh -i "<< oarsh_job_key_file <<" "<< grid_public_machine << " '" << karun.str() << " ' '' 2> " << errfile << "; rm -f " << errfile << " ) > /dev/null &";

        cout << "[front-end communicator] grid5000 command: " << cmd.str() << endl;

    }

    else if(where == GRID){
        
        if(task->getFiles().size() > 0){
            rsync << " rsync -avz ";
            for(i=0; i < task->getFiles().size(); i++)
                rsync << (task->getFiles()[i]).second << ' ';


            rsync << gridAddr << ':' << gridDataDir; //distination

            cout << "cmd: " << rsync.str() << endl;

            system(rsync.str().c_str());

        }

        karun <<" karun -t 1  -vvv -f " << grid_machine_file_addr << kaapiTaskDir << "/kaapiTask ";
        karun << gridDataDir << '/' << taskName  << ' ' << first << ' ' << last << ' ' << grain << ' '<< task->getName() << ' ';
        
        // adding arguments + adding new directory path to file arguments
        for(i=0; i < task->getArguments().size(); i++){
            if(task->getFiles().size() > 0)
                for(y=0; y < task->getFiles().size(); y++){

                    if(i == (task->getFiles()[i]).first)
                        karun << ' ' << gridDataDir  << '/' << task->getArguments()[i];
                    else
                        karun << ' ' << task->getArguments()[i];
                }
            else
                karun << ' ' << task->getArguments()[i];
        }

//dont forget (
        cmd << " ssh " << gridAddr << "' " << karun.str() << " '";// 2> " << errfile << "; rm -f " << errfile << " ) > /dev/null &";
    }

    //cout << "cmd: " <<  cmd.str() << endl;
    system(cmd.str().c_str());


}
//if errFile exists means either running. if the file size > 0, means we got an error. If file is removed,  then grid task is finished
e_grid_status frontendCommunicator::checkGridStatus(){
    ifstream f;
    unsigned long begin, end, size;
    f.open(errfile.c_str());
    if(!f.is_open()){

        return FINISH; //when the file is not there, it means the grid task is done (the file will eventually deleted)!!        
    }else{ 
        //compute the file size
        begin = f.tellg();
        f.seekg (0, ios::end);
        end = f.tellg();
        size =  end - begin;
        if(size > 0)//we got some errors
        {
            return FAILURE;
        }else
            return RUNNING;

    }
}
